package com.edushare.common.dao;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.cayenne.DataObjectUtils;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.query.SQLTemplate;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.query.SortOrder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.edushare.common.constants.ApplicationConstants;
import com.edushare.common.exceptions.EduShareRuntimeException;
import com.edushare.common.orm.ORMInterface;
import com.edushare.common.vo.Assignments;
import com.edushare.common.vo.CourseInstances;
import com.edushare.common.vo.FileDirTypes;
import com.edushare.common.vo.FileDirectories;
import com.edushare.common.vo.FilesUploaded;
import com.edushare.common.vo.UserAssignmentLink;
import com.edushare.common.vo.Users;

public class AssignmentsDAO extends CayenneBasedDAO {
	
	private static Log log = LogFactory.getLog(AssignmentsDAO.class);
	
	public AssignmentsDAO(ORMInterface ormInterface) {
		super(ormInterface);
	}
	
	public Assignments getAssignmentByAssignmentId(int assignmentId){
		log.debug("Getting Assignment for ID: "+ assignmentId);
		Assignments assgn = null;
		assgn = (Assignments)DataObjectUtils.objectForPK(
														getDataContext(),
														Assignments.class, 
														assignmentId);
		
		return assgn;
	}

	/**
	 * NOTE:: sortUserBy is not in use!
	 * @param assignmnetVO
	 * @param pageSize
	 * @param sortUsersBy
	 * @return List<Users>
	 */
	@SuppressWarnings("unchecked")
	public List<Users> getAssignmentSubmitters(Assignments asgn, int pageSize, int sortUsersBy) {
		
		String sql = 
			"select *  from users u, assignment_files af " +
			"where " +
				"u.user_id = af.uploader_id and " +
				"assignment_id = #bind($asgnId) " +
			"group by " +
				"user_name " +
			"order by " +
				"user_name asc";
		
		SQLTemplate sqlT = new SQLTemplate(Users.class, sql);
		HashMap parameters = new HashMap();
		parameters.put("asgnId", asgn.getAssignmentId());
		sqlT.setParameters(parameters);
		
		sqlT.setPageSize(pageSize);
		
		List users = getDataContext().performQuery(sqlT);
		return users;
	}

	@SuppressWarnings("unchecked")
	public List<Assignments> getAssignments(
								int courseInstanceId,
								int pageSize, 
								String sortByField, 
								int sortOrder) 
	{
		Expression exp = Expression.fromString("db:COURSE_INSTANCE_ID = $ciid");
		HashMap params = new HashMap();
		params.put("ciid", courseInstanceId);
		SelectQuery query = new SelectQuery(Assignments.class, exp.expWithParameters(params));
		
		SortOrder order = SortOrder.DESCENDING;
		
		if(sortOrder == ApplicationConstants.SORT_ORDER_ASCENDING){
			order = SortOrder.ASCENDING;
		}
		
		query.addOrdering(sortByField, order);
		query.setPageSize(pageSize);
		
		return (List<Assignments>)getDataContext().performQuery(query);
	}

	/**
	 * Please note, it does not create directories!
	 * Creates assingment and links to users
	 * @param courseInstanceId
	 * @param users 
	 * @return new assingment
	 */
	public Assignments createAssignment(Integer courseInstanceId, Users user, int status){
		
		//1. create a new assignment
		CourseInstances ci = new CourseInstancesDAO(ormInterface).getCourseInstanceById(courseInstanceId);
		Assignments asn = getDataContext().newObject(Assignments.class);
		asn.setCourseInstance(ci);
		asn.setCreationTimestamp(new Date());
		asn.setStatus(status);
		getDataContext().commitChanges();//we need ID
		
		//2. Link to user_assignment link table
		new UserAssignmentLinkDAO(ormInterface).linkToUsers(asn, ci, user);
				
		return asn;
	}
	
	/**
	 * 
	 * @param asn
	 * @return help directory
	 */
	public FileDirectories getHelpDirectory(Assignments asn){
		List<FileDirectories> fdl= getFileDirectory(asn, FileDirTypes.ASSIGNMENT_HELP);
		return fdl.get(0);
	}
	
	/**
	 * 
	 * @param asn
	 * @return submission directory
	 */
	public FileDirectories getSubmissionsDirectory(Assignments asn){
		List<FileDirectories> fdl= getFileDirectory(asn, FileDirTypes.ASSIGNMENT_SUBMISSION);
		return fdl.get(0);
	}
	
	/** 
	 * 
	 * @param asn
	 * @return Solution directory
	 */
	public FileDirectories getSolutionDirectory(Assignments asn){
		List<FileDirectories> fdl= getFileDirectory(asn, FileDirTypes.ASSIGNMENT_SOLN);
		return fdl.get(0);
	}
	
	/** gets directory of a given types else throws RTE */
	private List<FileDirectories> getFileDirectory(Assignments asn, String dirType){
		List<FileDirectories> fdl = new FileDirectoriesDAO(ormInterface).getAllDirectories(asn, dirType);
		if(fdl==null || fdl.size()<1){
			log.error("No Such Directory exists! Assignment= [id: "+ asn.getAssignmentId()+", type: "+asn.getObjectType()+"]; FileDirType = "+FileDirTypes.ASSIGNMENT_HELP);
			throw new EduShareRuntimeException("No such directory exists!");
		}
		return fdl;
	}
	
	/** Deletes 1. All the (three) directories and it's contents, 2. users association links, 3. the assignment */ 
	public void deleteAssignmentsCompletly(Assignments asn){
		//1. Delete directories
		new FileDirectoriesDAO(ormInterface).deleteDirectoryAndFiles(getHelpDirectory(asn));
		new FileDirectoriesDAO(ormInterface).deleteDirectoryAndFiles(getSolutionDirectory(asn));
		new FileDirectoriesDAO(ormInterface).deleteDirectoryAndFiles(getSubmissionsDirectory(asn));
		
		//2. Unregister users from link table
		Iterator<UserAssignmentLink> ualit = asn.getUsersLink().iterator();
		while(ualit.hasNext()){
			UserAssignmentLink tempLink = ualit.next();
			ualit.remove();
			getDataContext().deleteObject(tempLink);
		}
		//2.1 remove all grades
		new GradingItemDAO(ormInterface).removeGradingItem(asn.getGradingItem());
		
		//3. delete assignment
		getDataContext().deleteObject(asn);
		getDataContext().commitChanges();		
		
	}

	/** Returns all files uploaded by a user in <strong>Submissions directory</strong>. **/
	public List<FilesUploaded> getUserSubmissions(Users user, Assignments assignment) {
		return new FilesUploadedDAO(ormInterface).getFilesUploadedByUser(getSubmissionsDirectory(assignment),user);
	}
	
	/** Deletes all the files by the user uploaded to <strong>submissions directory</strong> */
	public void deleteUserSubmittedFiles(Users user, Assignments assignment){
		new FilesUploadedDAO(ormInterface).deleteFiles(getUserSubmissions(user, assignment));
	}

	/** resturns all assingments that are not in draft state */ 
	public List<Assignments> getNonDraftAssignments(CourseInstances courseInstance) {
		Expression exp = Expression.fromString("courseInstance = $cinstance and status != $stts");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("cinstance", courseInstance);
		params.put("stts", Assignments.ASSIGNMENT_STATUS_DRAFT);
		SelectQuery query = new SelectQuery(Assignments.class, exp.expWithParameters(params));
		query.addOrdering("status", SortOrder.DESCENDING);
		query.addOrdering("endDate", SortOrder.ASCENDING);
		return getDataContext().performQuery(query);
	}
	
	public HashMap<Integer, Integer> getStatusStat(CourseInstances courseInstance){

		HashMap<Integer, Integer> result = new HashMap<Integer, Integer>();
		String countQuery = "select status, #result('count(*)' 'int' 'stts') " +
							"from ASSIGNMENTS " +
							"where course_instance_id = #bind($ciId) " +
							"group by status";
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("ciId", courseInstance.getCourseInstanceId());
		SQLTemplate query = new SQLTemplate(UserAssignmentLink.class, countQuery);
		query.setFetchingDataRows(true);
		query.setParameters(params);
		List<Map> rows = (List<Map>)getDataContext().performQuery(query);
		if(rows!=null){
			for(Map m: rows){
				result.put((Integer)m.get("status"), (Integer)m.get("stts"));
			}
		}
		
		return result;
	}

	/**
	 * Used by Quartz job to update assignments daily at 0000 HRS and when application Starts/restarts
	 */
	public void updateAllAssignmentStatus() {
		log.debug("------- UPDATING ALL ASSIGNMENT STATUS --------");
		Date now = new Date();
		Expression exp = Expression.fromString("status == $sttso or status == $sttsl");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("sttso", Assignments.ASSIGNMENT_STATUS_OPEN);
		params.put("sttsl", Assignments.ASSIGNMENT_STATUS_LATE);
		SelectQuery query = new SelectQuery(Assignments.class, exp.expWithParameters(params));
		List<Assignments> assnList = getDataContext().performQuery(query);
		for(Assignments a: assnList){
			if(a.getStatus()== Assignments.ASSIGNMENT_STATUS_OPEN){
				if(a.getEndDate().before(now)){//if end date passed
					if(a.getAllowLate()==Assignments.ASSIGNMENT_ALLOW_LATE_SUBMIT && a.getLateSubmDate().after(now)){//if late submit is allowed and late submission date is after now set late else close
						a.setStatus(Assignments.ASSIGNMENT_STATUS_LATE);
					}else{
						a.setStatus(Assignments.ASSIGNMENT_STATUS_CLOSED);
						reconfigureAssignmentAfterClosing(a);
					}
				}
			}else if(a.getStatus() == Assignments.ASSIGNMENT_STATUS_LATE){
				if(a.getLateSubmDate().before(now)){
					a.setStatus(Assignments.ASSIGNMENT_STATUS_CLOSED);
					reconfigureAssignmentAfterClosing(a);
				}
			}
		}
		
		getDataContext().commitChanges();
	}

	private void reconfigureAssignmentAfterClosing(Assignments a) {
		//set access level of files to course students
		if(getSolutionDirectory(a)!=null){
			getSolutionDirectory(a).setReadPerm(FileDirectories.PERM_COURSE_STUDENT);
		}
	}

	public boolean isSolutionFilesVisible(Assignments assignment) {
		if(assignment.getStatus()== Assignments.ASSIGNMENT_STATUS_CLOSED){
			return true;
		}
		return false;
	}

	public List<Assignments> getAssignmentsByStatus(CourseInstances courseInstance, int status) {
		Expression exp = Expression.fromString("courseInstance = $ci and status = $stts");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("ci", courseInstance);
		params.put("stts", status);
		SelectQuery query = new SelectQuery(Assignments.class, exp.expWithParameters(params));
		return getDataContext().performQuery(query);
	}

}
